package biz

import (
	"context"
	"encoding/pem"
	"strings"

	"code.bydev.io/cht/customer/kyc_admin_service/internal/domain/kyc"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycadminapi"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/lark"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/meta"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/teller"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/utils/urpc/private"
	"code.bydev.io/frameworks/byone/kafka"
	"code.bydev.io/frameworks/sechub-go/api"
	"emperror.dev/errors"
	"git.bybit.com/svc/go/pkg/bdal"
	"git.bybit.com/svc/go/pkg/bdebug"
	"git.bybit.com/svc/go/pkg/benv"
	"git.bybit.com/svc/go/pkg/bmetric"
	"git.bybit.com/svc/go/pkg/bstd"
	"git.bybit.com/svc/go/pkg/bzap"
	"git.bybit.com/svc/mod/pkg/bsecret"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kms"
	"go.uber.org/zap"
)

func provideLogger() *zap.Logger {
	var logger *zap.Logger
	if config.InDebug() {
		l, err := zap.NewDevelopment(
			zap.AddCaller(),
		)
		bstd.MustNoError(err)
		logger = l
		bdebug.EnableDebugMode()
		bdebug.SetErrorHandler(bzap.LogErrorHandler(logger, "bdebug error"))
	} else {
		logger = bzap.NewFromViper(config.Viper())
	}

	return logger
}

func provideBEnv(ctx context.Context, logger *zap.Logger) *benv.Environment {
	return benv.NewEnvironment(ctx, logger)
}

func provideMetricEnv(environment *benv.Environment) *bmetric.Environment {
	return bmetric.NewEnvironment(environment, strings.ToLower(meta.ProjectCode))
}

func provideLarkRaiser() lark.LarkRaiser {
	return lark.New(config.GetString(config.KeyLarkAlarmURL))
}

func provideTeller(logger *zap.Logger, alarm lark.LarkRaiser) kycadminapi.Teller {
	return teller.New(logger, alarm)
}

func provideKycAdminSecHub() (*api.Sechub, error) {
	secHub := config.Sechub()
	if secHub.Client == nil {
		return nil, errors.Sentinel("secHub client is nil")
	}
	return secHub, nil
}

type kycSecHubWrapper struct{ secHub *api.Sechub }

func provideKycSecHub() (ksh kycSecHubWrapper, err error) {
	ksh.secHub = config.KycSechub()
	if ksh.secHub == nil {
		err = errors.Sentinel("KYC sechub nil")
	}
	return
}

type chtKycDBWrapper struct{ *bdal.Environment }

func provideChtKycDB(env *benv.Environment, c bdal.DefaultSqlxConfig) (chtKycDBWrapper, error) {
	o, err := bdal.EnvFromByoneConfig(env, c)
	if err != nil {
		return chtKycDBWrapper{}, err
	}
	return chtKycDBWrapper{Environment: o}, nil
}

type userDBWrapper struct{ *bdal.Environment }

func provideUserDB(env *benv.Environment, c bdal.DefaultSqlxConfig) (userDBWrapper, error) {
	o, err := bdal.EnvFromByoneConfig(env, c)
	if err != nil {
		return userDBWrapper{}, err
	}
	return userDBWrapper{Environment: o}, nil
}

func provideKafka(c kafka.ProducerConfig) kafka.Producer {
	return kafka.MustNewProducer(c)
}

func provideKycServiceGRPC(rpcClient *private.RPCClient) kyc.KycSvc {
	return kyc.NewKycClient(rpcClient.KycInternalClientAPI, rpcClient.KycInternalClientInfoAPI)
}

type InternalDataKey bsecret.DataKey

func provideInternalDataKey(k *kms.KMS) (InternalDataKey, error) {
	var kmsConfig config.KMSConfig
	err := config.Viper().UnmarshalKey("kms", &kmsConfig)
	if err != nil {
		return nil, err
	}

	keyID := kmsConfig.InternalKeyID
	cipher := kmsConfig.InternalCipher

	block, _ := pem.Decode([]byte(cipher))
	if block == nil {
		return nil, errors.Sentinel("internal bad cipher pem")
	}
	dataKey, err := bsecret.NewKMSDataKey(keyID, block.Bytes, k, 0)
	if err != nil {
		return nil, err
	}
	return dataKey, nil
}

func provideKMS(s *session.Session) *kms.KMS {
	return kms.New(s)
}

func provideAWSSession() (*session.Session, error) {
	return session.NewSession(&aws.Config{
		Region: aws.String(config.GetString(config.KeyAWSRegion)),
	})
}
